window.cqjs = window.cqjs || {};

cqjs.utils = {
    /** 用于检查给定的参数是否为一个纯粹的对象 */
    isPlainObject: function (obj) {
        // 通过 toString 方法判断对象是否为 [object Object]
        if (typeof obj !== 'object' || obj === null || obj.nodeType || obj === obj.window) {
            return false;
        }
        // 通过 constructor 属性判断对象是否由 Object 构造函数创建（纯粹对象）
        if (obj.constructor && !Object.prototype.hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf')) {
            return false;
        }
        return true;
    },
    /** 参考 jQuery.extend 方法，使用 defineMethod 处理 Function，实现类的继承方法可以通过 callParent 调用父类的方法 */
    apply: function () {
        let options;
        let name;
        let src;
        let copy;
        let copyIsArray;
        let clone;
        let target = arguments[0] || {};
        let i = 1;
        const length = arguments.length;
        let deep = false;
        if (typeof target === 'boolean') {
            deep = target;
            target = arguments[1] || {};
            i = 2;
        }
        if (typeof target !== 'object' && Object.prototype.toString.call(target) !== '[object Function]') {
            target = {};
        }
        if (length === i) {
            target = this;
            --i;
        }
        for (; i < length; i++) {
            if ((options = arguments[i]) != null) {
                for (name in options) {
                    if (name && name[0] === '$') {
                        continue;
                    }
                    src = target[name];
                    copy = options[name];
                    if (name === 'methods') {
                        copy.$class = target.$class;
                    }
                    if (target === copy) {
                        continue;
                    }
                    if (deep && copy && (cqjs.utils.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
                        if (copyIsArray) {
                            copyIsArray = false;
                            clone = src && Array.isArray(src) ? cqjs.utils.apply(deep, [], src) : [];
                        } else {
                            clone = src && cqjs.utils.isPlainObject(src) ? cqjs.utils.apply(deep, {}, src) : {};
                        }
                        if (copy.$class) {
                            clone.$class = copy.$class;
                        }
                        target[name] = cqjs.utils.apply(deep, clone, copy);
                    } else if (copy !== undefined) {
                        if (copy instanceof Function) {
                            cqjs.utils.defineMethod(target, name, copy);
                        } else {
                            target[name] = copy;
                        }
                    }
                }
            }
        }
        return target;
    },
    /** 定义方法 */
    defineMethod: function (owner, name, body) {
        const clone = function (method) {
            let prop;
            const newMethod = function () {
                return method.apply(this, arguments);
            };
            for (prop in method) {
                if (Object.hasOwnProperty.call(method, prop)) {
                    newMethod[prop] = method[prop];
                }
            }
            return newMethod;
        };
        if (body.$class) {
            const origin = body;
            body = clone(body);
            body.$origin = origin;
        }
        owner[name] = body;
        body.$name = name;
        body.$class = owner.$class;
    },
    /** 解析xml */
    parseXML: function (xmlStr) {
        return $('<root>' + xmlStr + '</root>');
    },
    /** cookie读写 */
    cookie: function (name, value, options) {
        if (typeof value !== 'undefined') {
            options = options || {};
            if (value === null) {
                value = '';
                options = $.extend({}, options);
                options.expires = -1;
            }
            let expires = '';
            if (options.expires && (typeof options.expires === 'number' || options.expires.toUTCString)) {
                let date;
                if (typeof options.expires === 'number') {
                    date = new Date();
                    date.setTime(date.getTime() + options.expires * 24 * 60 * 60 * 1000);
                } else {
                    date = options.expires;
                }
                expires = '; expires=' + date.toUTCString();
            }
            const path = options.path ? '; path=' + options.path : ';path=/';
            const domain = options.domain ? '; domain=' + options.domain : '';
            const secure = options.secure ? '; secure' : '';
            document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
        } else {
            let cookieValue = null;
            if (document.cookie && document.cookie != '') {
                const cookies = document.cookie.split(';');
                for (let i = 0; i < cookies.length; i++) {
                    const cookie = jQuery.trim(cookies[i]);
                    if (cookie.substring(0, name.length + 1) == name + '=') {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                        break;
                    }
                }
            }
            return cookieValue;
        }
    },
};

cqjs._components = {};
cqjs.component = function (name, define) {
    if (typeof define === "function") {
        define = define();
    }
    const clz = function () { };
    clz.$name = name;
    clz.$id = name;
    const prop = clz.prototype;
    const addBase = function (b) {
        const base = cqjs._components[b];
        if (base) {
            if (base.$name == name) {
                clz.$id = base.$id + '-ex';
            }
            clz.$bases.push(base);
            cqjs.utils.apply(true, prop, base.prototype);
            cqjs.utils.apply(true, clz.$statics, base.$statics);
        } else {
            throw new Error(name + ' 不能扩展未定义的 ' + b);
        }
    };
    prop.$class = clz;
    clz.$bases = [];
    if (typeof define.extends === 'string') {
        addBase(define.extends);
    } else if (Array.isArray(define.extends)) {
        for (let i = define.extends.length - 1; i >= 0; i--) {
            const b = define.extends[i];
            if (typeof b === 'string') {
                addBase(b);
            }
        }
    }
    cqjs.utils.apply(true, prop, define);
    cqjs._components[name] = clz;
    return clz;
};

cqjs.buildComponents = function () {
    function getOrigin(c) {
        if (c.$origin) {
            if (!c.$origin.$origin) {
                return c.$origin;
            } else {
                return getOrigin(c.$origin);
            }
        }
        return null;
    }
    for (let name in cqjs._components) {
        let clz = cqjs._components[name];
        if (!clz.prototype.methods) {
            clz.prototype.methods = {};
        }
        clz.prototype.methods.$getBase = function () {
            return clz.$bases;
        }
        clz.prototype.methods.callSuper = function () {
            if (!this.$super) {
                this.$super = {};
            }
            const stack = new Error().stack.split('\n')[3].trim().split(/\s+/)[1].trim().split('.');
            const name = stack[stack.length - 1];
            const bases = this.$super[name]?.$bases || getOrigin(this[name])?.$class.$bases || this.$getBase();
            for (let i = bases.length - 1; i >= 0; i--) {
                let baseMethod = bases[i].prototype?.methods?.[name];
                if (baseMethod) {
                    const sp = this.$super[name];
                    this.$super[name] = bases[i];
                    if (baseMethod.$origin) {
                        this.$super[name] = getOrigin(baseMethod).$class;
                        baseMethod = baseMethod.$origin;
                    }
                    const result = baseMethod.apply(this, arguments);
                    this.$super[name] = sp;
                    return result;
                }
            }
        }
        Vue.component(name, clz.prototype);
    }
};

// To reduce overhead on call of the bound fn we have two flavors based on
// whether we have args to prepend or not:
const bind = function (me) {
    const method = this;
    const fn = function () {
        return method.apply(me, arguments);
    };
    fn.$class = method.$class;
    fn.$origin = method.$origin;
    return fn;
};
Function.prototype.bind = bind;


(function () {
    if (typeof String.prototype.replaceAll === 'undefined') {
        String.prototype.replaceAll = function (match, replace) {
            return this.replace(new RegExp(match, 'g'), () => replace);
        };
    }
    /** 字符翻译，如 "名称".t() */
    String.prototype.t = function () {
        const str = String(this);
        if (!str || this == window) {
            return str;
        }
        if (!top.window.langData) {
            const lang = cqjs.utils.cookie('ctx_lang');
            const data = localStorage.getItem('lang-' + lang);
            top.window.langData = data ? JSON.parse(data) : {};
        }
        const value = top.window.langData[str];
        if (!value && top.window.isDebug) {
            if (!top.window.langCollect) {
                top.window.langCollect = {};
            }
            top.window.langCollect[str] = 0;
        }
        return value || str;
    };
    /** 从数组移除指定项，如 [1,2].remove(1) */
    Array.prototype.remove = function (e) {
        const idx = this.indexOf(e);
        if (idx > -1) {
            this.splice(idx, 1);
        }
    };
    Date.prototype.format = function (fmt) {
        const o = {
            'M+': this.getMonth() + 1, // 月份
            'D+': this.getDate(), // 日
            'd+': this.getDate(), // 日
            'h+': this.getHours() % 12 == 0 ? 12 : this.getHours() % 12, // 小时
            'H+': this.getHours(), // 小时
            'm+': this.getMinutes(), // 分
            's+': this.getSeconds(), // 秒
            'q+': Math.floor((this.getMonth() + 3) / 3), // 季度
            S: this.getMilliseconds(), // 毫秒
        };
        if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
        if (/(Y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
        for (const k in o) if (new RegExp('(' + k + ')').test(fmt)) fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length));
        return fmt;
    };
})();